package com.zboin.framework.serve;

import com.zboin.framework.core.SpringUtils;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author zhboom
 * @date 2023/8/29 0:05
 */
@Slf4j
public class ThreadPoolServe {

    private static final String DEFAULT_POOL_NAME = "zboin-thread-pool-";
    private static final String DELAY_POOL_NAME = "zboin-delay-pool-";

    /**
     * 系统默认线程池
     */
    private final ThreadPoolExecutor EXECUTOR;
    /**
     * 延迟任务执行线程池
     */
    private final ScheduledThreadPoolExecutor SCHEDULED_EXECUTOR;


    private static final class InstanceHolder {
        private static ThreadPoolServe INSTANCE = null;
    }

    private ThreadPoolServe() {
        // 系统默认线程池初始化
        int corePoolSize = Integer.parseInt(SpringUtils.getRequiredProperty("zboin.thread-pool.core-size"));
        int maxPoolSize = Integer.parseInt(SpringUtils.getRequiredProperty("zboin.thread-pool.max-size"));
        EXECUTOR = new ThreadPoolExecutor(corePoolSize, maxPoolSize, 10L, TimeUnit.SECONDS,
                new SynchronousQueue<>(), new NamedThreadFactory(DEFAULT_POOL_NAME),
                new ThreadPoolExecutor.CallerRunsPolicy());

        // 延迟任务线程池初始化
        int delayCorePoolSize = Integer.parseInt(SpringUtils.getRequiredProperty("zboin.thread-pool.delay-core-size"));
        SCHEDULED_EXECUTOR = new ScheduledThreadPoolExecutor(delayCorePoolSize,
                new NamedThreadFactory(DELAY_POOL_NAME),
                new ThreadPoolExecutor.CallerRunsPolicy());
    }

    public static ThreadPoolServe getInstance() {
        ThreadPoolServe service = InstanceHolder.INSTANCE;
        if (service == null) {
            synchronized (ThreadPoolServe.class) {
                service = InstanceHolder.INSTANCE;
                if (service == null) {
                    InstanceHolder.INSTANCE = new ThreadPoolServe();
                    service = InstanceHolder.INSTANCE;
                }
            }
        }
        return service;
    }


    /**
     * 提交一个任务到线程池中并立即执行
     * <p>
     *     如果当前线程池中的任务已经达到上限，则该任务在原有线程中直接执行
     * </p>
     * @param runnable 执行的任务
     */
    public void executeTask(Runnable runnable) {
       EXECUTOR.execute(runnable);
    }

    /**
     * 提交一个可以获取返回结果的任务到线程池中立即执行，并返回一个 Future 用于等待任务结果
     * <p>
     *     如果当前线程池中的任务已经达到上限，则该任务在原有线程中直接执行
     * </p>
     * @param callable 提交的任务
     * @param <V> 任务返回数据类型
     * @return 一个 Future 对象，用于等待任务执行的结果
     */
    public <V> Future<V> executeTask(Callable<V> callable) {
        return EXECUTOR.submit(callable);
    }

    /**
     * 提交一个延时执行的任务
     * @param runnable 执行的任务
     * @param delay 延时时间
     * @param unit 时间单位
     */
    public void executeDelayTask(Runnable runnable, long delay, TimeUnit unit) {
        if (delay == 0) {
            executeTask(runnable);
        }
        else {
            SCHEDULED_EXECUTOR.schedule(runnable, delay, unit);
        }
    }

    /**
     * 提交一个延迟执行的任务，并返回 Future 对象用与等待返回结果
     * @param callable 执行的任务
     * @param delay 延时时间
     * @param unit 时间单位
     * @param <V> 返回数据类型
     * @return 一个 Future 对象，用于等待任务执行的结果
     */
    public <V> Future<V> executeDelayTask(Callable<V> callable, long delay, TimeUnit unit) {
        if (delay == 0) {
            return executeTask(callable);
        }
        return SCHEDULED_EXECUTOR.schedule(callable, delay, unit);
    }

    private class NamedThreadFactory implements ThreadFactory {

        private final ThreadGroup group;
        private final AtomicInteger threadNumber = new AtomicInteger(1);
        private final String namePrefix;

        NamedThreadFactory(String namePrefix) {
            @SuppressWarnings("removal")
             SecurityManager s = System.getSecurityManager();
            group = (s != null) ? s.getThreadGroup() :
                    Thread.currentThread().getThreadGroup();

            this.namePrefix = namePrefix;
        }

        @Override
        public Thread newThread(Runnable r) {
            Thread t = new Thread(group, r,
                    namePrefix + threadNumber.getAndIncrement(),
                    0);
            if (t.isDaemon())
                t.setDaemon(false);
            if (t.getPriority() != Thread.NORM_PRIORITY)
                t.setPriority(Thread.NORM_PRIORITY);
            return t;
        }
    }

    private record NamedRejectHandler(String name) implements RejectedExecutionHandler {

        @Override
            public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
                log.warn("thread pool [{}] abort task.", name);
            }
        }
}
